#include <HTTPClient.h>
#include <WiFiClient.h>
#include "esp_camera.h"
#include <WiFi.h>
#include <ArduinoJson.h>

/*********************需要修改的地方**********************/
const char* ssid = "nan";           //WIFI名称
const char* password = "12345678";     //WIFI密码
int capture_interval = 10*1000;        // 默认20秒上传一次，可更改（本项目是自动上传，如需条件触发上传，在需要上传的时候，调用take_send_photo()即可）
const char*  post_url = "http://images.bemfa.com/upload/v1/upimages.php"; // 默认上传地址
const char*  uid = "9d7530867cde5a0c2379511a529b4f85";    //用户私钥，巴法云控制台获取
const char*  topic = "v";     //主题名字，可在控制台新建
bool sentWechat = true;               //是否推送到微信，默认不推送，true 为推送。需要在控制台先绑定微信，不然推送不到
const char*  wechatMsg = "图片，点击可以查看";     //推送到微信的消息，可随意修改，修改为自己需要发送的消息
/********************************************************/


/*------------------------------------------------------*/
//平台接入服务器地址ndp.nlecloud.com或IP 117.78.1.201
#define TCP_SERVER_ADDR "ndp.nlecloud.com"
//服务器端口，tcp新大陆物联TCP端口8600,8700,8800
#define TCP_SERVER_PORT "8600"


//WIFI名称，区分大小写，不要写错  
#define DEFAULT_STASSID  "nan"   //修改为自己WIFI
//WIFI密码
#define DEFAULT_STAPSW   "12345678"


//设置上传速率5s（1s<=upDataTime<=60s）
#define upDataTime 5*1000

//最大字节数
#define MAX_PACKETSIZE 512
//设置心跳值10s
#define KEEPALIVEATIME 10*1000

//tcp客户端相关初始化，默认即可
WiFiClient TCPclient;
String TcpClient_Buff = "";
unsigned int TcpClient_BuffIndex = 0;
unsigned long TcpClient_preTick = 0;
unsigned long preHeartTick = 0;//心跳
unsigned long preTCPStartTick = 0;//连接
bool preTCPConnected = false;

char res[100];
const char* url;
/*------------------------------------------------------*/

bool internet_connected = false;
long current_millis;
long last_capture_millis = 0;

// CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM      0
#define SIOD_GPIO_NUM     26
#define SIOC_GPIO_NUM     27
#define Y9_GPIO_NUM       35
#define Y8_GPIO_NUM       34
#define Y7_GPIO_NUM       39
#define Y6_GPIO_NUM       36
#define Y5_GPIO_NUM       21
#define Y4_GPIO_NUM       19
#define Y3_GPIO_NUM       18
#define Y2_GPIO_NUM        5
#define VSYNC_GPIO_NUM    25
#define HREF_GPIO_NUM     23
#define PCLK_GPIO_NUM     22

void setup()
{
  Serial.begin(115200);

  if (init_wifi()) // Connected to WiFi
  { 
    internet_connected = true;
    Serial.println("Internet connected");
  }

  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  //init with high specs to pre-allocate larger buffers
  if (psramFound()) 
  {
    config.frame_size = FRAMESIZE_UXGA;//分辨率默认配置：config.frame_size = FRAMESIZE_UXGA;（1600 x 1200）
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } 
  else 
  {
    config.frame_size = FRAMESIZE_SVGA;//（800 x 600）
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }

  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) 
  {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }
}

/********初始化WIFI*********/
bool init_wifi()
{
  int connAttempts = 0;
  Serial.println("\r\nConnecting to: " + String(ssid));
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED ) 
  {
    delay(500);
    Serial.print(".");
    if (connAttempts > 10) return false;
    connAttempts++;
  }
  return true;
}

/******************推送图片*******************/
static esp_err_t take_send_photo()
{
    //初始化相机并拍照
    Serial.println("Taking picture...");
    camera_fb_t * fb = NULL;
    fb = esp_camera_fb_get();
    if (!fb) 
    {
      Serial.println("Camera capture failed");
      return ESP_FAIL;
    }
  
    HTTPClient http;
    //设置请求url
    http.begin(post_url);
    
    //设置请求头部信息
    http.addHeader("Content-Type", "image/jpg");
    http.addHeader("Authorization", uid);
    http.addHeader("Authtopic", topic);
    if(sentWechat)//判断是否需要推送到微信
    { 
      http.addHeader("Wechatmsg", wechatMsg);  //设置 http 请求头部信息
    }
    //发起请求，并获取状态码
    int httpResponseCode = http.POST((uint8_t *)fb->buf, fb->len);
    
    if(httpResponseCode==200)
    {
        //获取post请求后的服务器响应信息，json格式
        String response = http.getString();  //Get the response to the request
        Serial.print("Response Msg:");
        Serial.println(response);           // 打印服务器返回的信息
        
        //json数据解析
        StaticJsonDocument<200> doc;
        DeserializationError error = deserializeJson(doc, response);
        if (error) 
        {
          Serial.print(F("deserializeJson() failed: "));
          Serial.println(error.c_str());
        }
        url = doc["url"];
        
        Serial.print("Get URL:");
        Serial.println(url);//打印获取的URL
    }
    else
    {
        //错误请求
        Serial.print("Error on sending POST: ");
        Serial.println(httpResponseCode);
    }
   
    Serial.print("HTTP Response code: ");
    Serial.println(httpResponseCode);
  
    //清空数据
    esp_camera_fb_return(fb);  
    //回收下次再用
    http.end();
//    Serial.println("url的内容：");
//    Serial.println(url);

    strcpy(res,url);
    
//    Serial.println("res的内容：");
//    Serial.println(res);
}

/*-------------------新大陆部分---------------------*/
/*
  *发送数据到TCP服务器
 */
void sendtoTCPServer(String p)
{
  if (!TCPclient.connected()) 
  {
    Serial.println("Client is not readly");
    return;
  }
  TCPclient.print(p);
  Serial.println("[Send to TCPServer]:String");
  Serial.println(p);
}

/*
  *初始化和服务器建立连接
*/
void startTCPClient()
{
  if(TCPclient.connect(TCP_SERVER_ADDR, atoi(TCP_SERVER_PORT)))
  {
    Serial.print("\nConnected to server:");
    Serial.printf("%s:%d\r\n",TCP_SERVER_ADDR,atoi(TCP_SERVER_PORT));
    DynamicJsonDocument doc(256);

    String Start_put = "{\"t\":\"1\",\"device\":\"temputer\",\"Key\":\"83dffa8331124bb2a483fcf7dfa5246e\",\"ver\":\"v1.1\"}"; //替换成自己的
    deserializeJson(doc, Start_put);  //创建数据
    sendtoTCPServer(Start_put);
    preTCPConnected = true;
    preHeartTick = millis();
    TCPclient.setNoDelay(true);
  }
  else
  {
    Serial.print("Failed connected to server:");
    Serial.println(TCP_SERVER_ADDR);
    TCPclient.stop();
    preTCPConnected = false;
  }
  preTCPStartTick = millis();
}

/*
  *检查数据，发送心跳
*/
void doTCPClientTick()
{
 //检查是否断开，断开后重连
  if(WiFi.status() != WL_CONNECTED) 
  return;

  if (!TCPclient.connected()) 
  {//断开重连

    if(preTCPConnected == true)
    {
      preTCPConnected = false;
      preTCPStartTick = millis();
      Serial.println();
      Serial.println("TCP Client disconnected.");
      TCPclient.stop();
    }
    else if(millis() - preTCPStartTick > 1*1000)//重新连接
    startTCPClient();
  }
  else
  {
    if (TCPclient.available()) //收数据
    {
      char c =TCPclient.read();
      TcpClient_Buff +=c;
      TcpClient_BuffIndex++;
      TcpClient_preTick = millis();
      
      if(TcpClient_BuffIndex>=MAX_PACKETSIZE - 1)
      {
        TcpClient_BuffIndex = MAX_PACKETSIZE-2;
        TcpClient_preTick = TcpClient_preTick - 200;
      }
      preHeartTick = millis();
    }
    if(millis() - preHeartTick >= KEEPALIVEATIME)//保持心跳
    {
      preHeartTick = millis();
      Serial.println("--Keep alive:");
      sendtoTCPServer("$#AT#\r\n");
    }
  }
  if((TcpClient_Buff.length() >= 1) && (millis() - TcpClient_preTick>=200))
  {//data ready
    TCPclient.flush();
    Serial.println("Buff");
    Serial.println(TcpClient_Buff);

    TcpClient_Buff="";
    TcpClient_BuffIndex = 0;
  }

  //上传数据
   if (millis() - preHeartTick >= upDataTime) 
   { //上传数据

      Serial.println("res的内容：");
      Serial.println(res);
      preHeartTick = millis();

      /*********************数据上传*******************/
      DynamicJsonDocument doc(1024);
      
      const char ch = '-';
      char *ret;
      ret = strchr(res, ch);
      String input = "{\"t\":\"3\",\"datatype\":1,\"datas\":{\"camera\":ret}}"; 
      Serial.println(input);   //{"t":"3","datatype":1,"datas":{"camera":ret}}
      deserializeJson(doc, input);  //创建数据
      JsonObject obj = doc.as<JsonObject>();
        
      obj["datas"]["camera"]= ret;
      String output;
      serializeJson(doc, output);
      Serial.println(output);
      sendtoTCPServer(output); //输出
      //Serial.println(output);
    }
}

void startSTA()
{
  WiFi.disconnect();
  WiFi.mode(WIFI_STA);
  WiFi.begin(DEFAULT_STASSID, DEFAULT_STAPSW);
}

/**************************************************************************
                                 WIFI
***************************************************************************/
/*
  WiFiTick
  检查是否需要初始化WiFi
  检查WiFi是否连接上，若连接成功启动TCP Client
  控制指示灯
*/
void doWiFiTick()
{
  static bool startSTAFlag = false;
  static bool taskStarted = false;
  static uint32_t lastWiFiCheckTick = 0;

  if (!startSTAFlag) 
  {
    startSTAFlag = true;
    startSTA();
    Serial.printf("Heap size:%d\r\n", ESP.getFreeHeap());
  }

  //未连接1s重连
  if ( WiFi.status() != WL_CONNECTED ) 
  {
    if (millis() - lastWiFiCheckTick > 1000) 
    {
      lastWiFiCheckTick = millis();
    }
  }
  //连接成功建立
  else 
  {
    if (taskStarted == false) 
    {
      taskStarted = true;
      Serial.print("\r\nGet IP Address: ");
      Serial.println(WiFi.localIP());
      startTCPClient();
    }
  }
}

/*----------------------------------------------------------------------*/

void loop()
{
  // TODO check Wifi and reconnect if needed

  //定时发送到巴法云
  current_millis = millis();
  if (current_millis - last_capture_millis > capture_interval) // 当前时间减去上次时间大于20S就执行拍照上传函数
  { 
    last_capture_millis = millis();
    take_send_photo(); //拍照上传函数，在需要的地方调用即可，这里是定时拍照
  }
  //上传数据到新大陆
  delay(500);
  doWiFiTick();
  doTCPClientTick();
}
